/**
 * 
 */
package jp.tkym.labs.store.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import jp.tkym.labs.store.ormap.OrMapObjectProvider;
import jp.tkym.labs.util.beankey.BeanKey;

public class PreparedStatementExecutor{
	private PreparedStatementCache statementCache;
	private PreparedStatementBinder binder;
	
	public PreparedStatementExecutor(Connection connection){
		statementCache = new PreparedStatementCache(connection);
		binder = new PreparedStatementBinder();
	}
	
	public <T> void drop(Class<T> cls) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.DROP, cls);
		execute(ps);
	}
	
	public <T> void create(Class<T> cls) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.CREATE, cls);
		execute(ps);
	}
	
	public <T> void insert(T entity) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.INSERT, classOf(entity));
		binder.bindBean(PreparedStatementType.INSERT, ps, entity);
		executeUpdate(ps);
	}

	public <T> void update(T entity) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.UPDATE, classOf(entity));
		binder.bindBean(PreparedStatementType.UPDATE, ps, entity);
		executeUpdate(ps);
	}
	
	public <T> void delete(Class<T> cls, BeanKey beanKey) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.DELETE, cls);
		binder.bindValues(ps, beanKey.values());
		executeUpdate(ps);
	}
	
	public <T> void delete(T entity) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.DELETE, classOf(entity));
		binder.bindBean(PreparedStatementType.DELETE, ps, entity);
		executeUpdate(ps);
	}
	
	public <T> PreparedStatementQuery<T> getAll(Class<T> cls) throws SQLException{
		return nativeEntityQuery(cls, "");
	}
	
	public <T> PreparedStatementQuery<T> nativeEntityQuery(Class<T> cls, String laterSql, Object... values) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.ENTITY, cls, laterSql);
		binder.bindValues(ps, values);
		return new PreparedStatementQuery<T> (cls, ps);
	}
	
	public <T> PreparedStatementQuery<T> nativeKeyQuery(Class<T> cls, String laterSql, Object... values) throws SQLException{
		PreparedStatement ps = statementCache.getStatement(PreparedStatementType.KEY, cls, laterSql);
		binder.bindValues(ps, values);
		String [] keyProperties = OrMapObjectProvider.getInstance().get(cls).keyProperties();
		return new PreparedStatementQuery<T> (cls, ps, keyProperties);
	}
	       
	@SuppressWarnings("unchecked")
	public <T> Class<T> classOf(T entity){
		return (Class<T>) entity.getClass();
	}
	
	private void execute(PreparedStatement ps) throws SQLException{
		ps.executeUpdate();
	}
	
	private void executeUpdate(PreparedStatement ps) throws SQLException{
		ps.executeUpdate();
		if(ps.getUpdateCount()==0) throw new PreparedStatementException("update count = "+ps.getUpdateCount());
	}
}